/* Copyright (c) 2009 SpringShowcase, Inc.
 * All right reserved.
 * http://code.google.com/p/springshowcase/
 * This software is the confidential and proprietary information of BeyondWeb
 * , Inc. You shall not disclose such Confidential Information and
 * shall use it only in accordance with the terms of the license agreement
 * you entered into with BeyondWeb.
 *
 * Revision History
 * Author              Date             Description
 * ------------------   --------------    ------------------
 *  taeki.kim           2010. 4. 2.        
*/

package org.springshowcase.core.commons.utils;

import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtilsBean;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 오브젝트간의 멤버 변수가 같은지 비교 하는 유틸리티
 * 서로 다른 오브젝트의 멤버 변수가 같은지 비교한다.
 * 기본적으로 org/apache/commons/lang/builder/EqualsBuilder 클래스를 상속 
 * 받아서 메서드를 재사용 할 수 있다.
 * 
 * @author kim.tae.ki
 * @version
 * @see
 * 
 * This file contains the declarations of String class and its helper classes.
 */
public class EqualsUtil extends EqualsBuilder {

    // ---------------------------------------------------------------------
    // Class Variable Declaration
    // ---------------------------------------------------------------------
    
	/** The Constant log. */
	private static final Logger logger = LoggerFactory.getLogger(EqualsUtil.class);

	// ---------------------------------------------------------------------
    // Constructor Method Declaration
    // ---------------------------------------------------------------------

	// ---------------------------------------------------------------------
    // DI Method (setter,getter) Declaration
    // ---------------------------------------------------------------------
	
	/**
	 * Gets the log.
	 * 
	 * @return the log
	 */
	public static Logger getLog() {
		return logger;
	}

	// ---------------------------------------------------------------------
    // this Method (public/private) Declaration
    // ---------------------------------------------------------------------

	/**
	 * 두 오브젝트의 멤버 변수를 같은지 비교 한다.
	 * 두 오브젝트이 멤버 변수를 비교 한다. includeFields에 값을 할당 하면
	 * 해당 속성명만 비교 한다.
	 * 
	 * @param lhs the lhs
	 * @param rhs the rhs
	 * @param includeFields the include fields
	 * 
	 * @return true, if successful
	 * 
	 * @throws Exception the exception
	 */
	public static boolean reflectionEqualsByIncludeFields(java.lang.Object lhs,
			java.lang.Object rhs, String[] includeFields) throws Exception {

		String strKey;
		String[] strExcludeFields;
		int propSize = 0;
		int loopCount = 0;

		if (includeFields == null || includeFields.length <= 0) {
			return reflectionEquals(lhs, rhs);
		}
		PropertyUtilsBean pub = new PropertyUtilsBean();
		// 도메인의 property 정보를 가져 온다.
		Map<?, ?> proMap = pub.describe(rhs);
		// includeFields에 해당되는 property 정보를 삭제 한다.

		for (int count = 0; count < includeFields.length; count++) {
			proMap.remove(includeFields[count]);
		}
		// exclude 필드가 없을 경우 일반 reflectionEquals 처리
		if (proMap == null || proMap.size() <= 0) {
			return reflectionEquals(lhs, rhs);
		}
		propSize = proMap.size();

		// exclude필드를 담을 배열
		strExcludeFields = new String[propSize];
		// exclude필들 생성
		for (Iterator<?> iterator = proMap.keySet().iterator(); iterator
				.hasNext();) {
		    strKey = (String) iterator.next();
		    strExcludeFields[loopCount++] = strKey;
		}
		return reflectionEquals(lhs, rhs, strExcludeFields);
	}

	/**
	 * 두 오브젝트의 멤버 변수를 같은지 비교 한다.
     * 두 오브젝트이 멤버 변수를 비교 한다. includeFields에 값을 할당 하면
     * 해당 속성명을 제외 하고 비교 한다.
	 * 
	 * @param lhs the lhs
	 * @param rhs the rhs
	 * @param excludeFields the exclude fields
	 * 
	 * @return true, if successful
	 */
	public static boolean reflectionEqualsByExcludeFields(java.lang.Object lhs,
			java.lang.Object rhs, java.lang.String[] excludeFields) {
		return reflectionEquals(lhs, rhs, excludeFields);
	}

	/**
	 * 두개의 List 오브젝트가 같은지 비교 한다.
	 * loop을 돌면서 각각의 리스트의 엘리먼트가 같은지 비교 한다.
	 * 
	 * @param target the target
	 * @param compare the compare
	 * 
	 * @return true, if successful
	 * 
	 * @throws Exception the exception
	 */
	public static boolean reflectionEqualsList(List<?> target, List<?> compare)
			throws Exception {
		return reflectionEqualsListByCase(target, compare, null, null);
	}

	/**
	 * 두개의 List 오브젝트가 같은지 비교 한다.
     * loop을 돌면서 각각의 리스트의 엘리먼트가 같은지 비교 한다.
     * excludeFields에 선언된 속성명은 제외 된다.
	 * 
	 * @param target the target
	 * @param compare the compare
	 * @param excludeFields the exclude fields
	 * 
	 * @return true, if successful
	 * 
	 * @throws Exception the exception
	 */
	public static boolean reflectionEqualsListByExcludeFields(List<?> target,
			List<?> compare, String[] excludeFields) throws Exception {
		return reflectionEqualsListByCase(target, compare, excludeFields, null);
	}

	/**
     * 두개의 List 오브젝트가 같은지 비교 한다.
     * loop을 돌면서 각각의 리스트의 엘리먼트가 같은지 비교 한다.
     * includeFields에 선언된 속성명만 체크 한다.
	 * 
	 * @param target the target
	 * @param compare the compare
	 * @param includeFields the include fields
	 * 
	 * @return true, if successful
	 * 
	 * @throws Exception the exception
	 */
	public static boolean reflectionEqualsListByIncludeFields(List<?> target,
			List<?> compare, String[] includeFields) throws Exception {
		return reflectionEqualsListByCase(target, compare, null, includeFields);
	}

	/**
	 * 두 개의 리스트 중에서 작은 것 기준으로 리스트를 비교 한다.
	 * 
	 * @param target the target
	 * @param compare the compare
	 * @param excludeFields the exclude fields
	 * @param includeFields the include fields
	 * 
	 * @return true, if successful
	 * 
	 * @throws Exception the exception
	 */
	private static boolean reflectionEqualsListByCase(List<?> target,
			List<?> compare, String[] excludeFields, String[] includeFields)
			throws Exception {

		Object temp1;
		Object temp2;
		boolean equal = true;
		int loopCnt = 0;

		if (target == null || compare == null) {
			return false;
		}
		// 두 리스트 중에서 loop count를 작은 것 기준으로 한다.
		loopCnt = target.size();
		if (loopCnt > compare.size()) {
			loopCnt = compare.size();
		}

		for (int i = 0; i < loopCnt; i++) {
			temp1 = target.get(i);
			temp2 = compare.get(i);

			if (excludeFields == null && includeFields == null) {
				if (!reflectionEquals(temp1, temp2)) {
					equal = false;
					break;
				}
			}

			if (includeFields != null && excludeFields == null) {
				if (!reflectionEqualsByIncludeFields(temp1, temp2,
						includeFields)) {
					equal = false;
					break;
				}
			}

			if (includeFields == null && excludeFields != null) {
				if (!reflectionEquals(temp1, temp2, excludeFields)) {
					equal = false;
					break;
				}
			}
		}
		return equal;
	}

	// ---------------------------------------------------------------------
    // interface implement Method (public/private) Declaration
    // ---------------------------------------------------------------------
    // ---------------------------------------------------------------------
    // abstract implement Method (public/private) Declaration
    // ---------------------------------------------------------------------
    // ---------------------------------------------------------------------
    // abstract Method (public/private) Declaration
    // ---------------------------------------------------------------------
}
